﻿#region Using directives

using System.Collections.Generic;
using System.Text;
using System.Web;

#endregion

namespace MvcSiteMapProvider.Web.Html
{
    /// <summary>
    /// MvcSiteMapHtmlHelper extension methods
    /// </summary>
    public static class SiteMapHelper
    {
        /// <summary>
        /// Source metadata
        /// </summary>
        private static Dictionary<string, object> SourceMetadata = new Dictionary<string, object> { { "HtmlHelper", typeof(SiteMapHelper).FullName } };

        /// <summary>
        /// Build a sitemap tree, based on the MvcSiteMap
        /// </summary>
        /// <param name="helper">MvcSiteMapHtmlHelper instance</param>
        /// <returns>Html markup</returns>
        public static string SiteMap(this MvcSiteMapHtmlHelper helper)
        {
            return SiteMap(helper, System.Web.SiteMap.RootNode, "");
        }

        /// <summary>
        /// Build a sitemap tree, based on the MvcSiteMap
        /// </summary>
        /// <param name="helper">MvcSiteMapHtmlHelper instance</param>
        /// <param name="rootNode">Root node</param>
        /// <returns>Html markup</returns>
        public static string SiteMap(this MvcSiteMapHtmlHelper helper, SiteMapNode rootNode)
        {
            return SiteMap(helper, rootNode, "");
        }

        /// <summary>
        /// Build a sitemap tree, based on the MvcSiteMap
        /// </summary>
        /// <param name="helper">MvcSiteMapHtmlHelper instance</param>
        /// <param name="cssClass">SiteMap CSS class</param>
        /// <returns>Html markup</returns>
        public static string SiteMap(this MvcSiteMapHtmlHelper helper, string cssClass)
        {
            return SiteMap(helper, System.Web.SiteMap.RootNode, cssClass);
        }

        /// <summary>
        /// Build a sitemap tree, based on the MvcSiteMap
        /// </summary>
        /// <param name="helper">MvcSiteMapHtmlHelper instance</param>
        /// <param name="renderRootInChildLevel">if set to <c>true</c> render root in child level.</param>
        /// <returns>Html markup</returns>
        public static string SiteMap(this MvcSiteMapHtmlHelper helper, bool renderRootInChildLevel)
        {
            return SiteMap(helper, renderRootInChildLevel, "");
        }

        /// <summary>
        /// Build a sitemap tree, based on the MvcSiteMap
        /// </summary>
        /// <param name="helper">MvcSiteMapHtmlHelper instance</param>
        /// <param name="renderRootInChildLevel">if set to <c>true</c> render root in child level.</param>
        /// <param name="cssClass">SiteMap CSS class</param>
        /// <returns>Html markup</returns>
        public static string SiteMap(this MvcSiteMapHtmlHelper helper, bool renderRootInChildLevel, string cssClass)
        {
            return SiteMap(helper, System.Web.SiteMap.RootNode, renderRootInChildLevel, cssClass, true);
        }

        /// <summary>
        /// Build a sitemap tree, based on the MvcSiteMap
        /// </summary>
        /// <param name="helper">MvcSiteMapHtmlHelper instance</param>
        /// <param name="rootNode">Root node</param>
        /// <param name="cssClass">SiteMap CSS class</param>
        /// <returns>Html markup</returns>
        public static string SiteMap(this MvcSiteMapHtmlHelper helper, SiteMapNode rootNode, string cssClass)
        {
            return SiteMap(helper, rootNode, cssClass, true);
        }

        /// <summary>
        /// Build a sitemap tree, based on the MvcSiteMap
        /// </summary>
        /// <param name="helper">MvcSiteMapHtmlHelper instance</param>
        /// <param name="rootNode">Root node</param>
        /// <param name="cssClass">SiteMap CSS class</param>
        /// <param name="renderListTag">Render a list tag?</param>
        /// <returns>Html markup</returns>
        public static string SiteMap(this MvcSiteMapHtmlHelper helper, SiteMapNode rootNode, string cssClass, bool renderListTag)
        {
            return SiteMap(helper, rootNode, false, cssClass, renderListTag);
        }

        /// <summary>
        /// Build a sitemap tree, based on the MvcSiteMap
        /// </summary>
        /// <param name="helper">MvcSiteMapHtmlHelper instance</param>
        /// <param name="rootNode">Root node</param>
        /// <param name="renderRootInChildLevel">if set to <c>true</c>, root node will be rendered within the child level (flattened structure).</param>
        /// <param name="cssClass">SiteMap CSS class</param>
        /// <param name="renderListTag">Render a list tag?</param>
        /// <returns>Html markup</returns>
        public static string SiteMap(this MvcSiteMapHtmlHelper helper, SiteMapNode rootNode, bool renderRootInChildLevel, string cssClass, bool renderListTag)
        {
            // String builder
            var sb = new StringBuilder();

            // Create unordered list tag
            if (renderListTag)
            {
                sb.Append("<ul");
                if (!string.IsNullOrEmpty(cssClass))
                {
                    sb.Append(string.Format(" class=\"{0}\"", cssClass));
                }
                sb.AppendLine(">");
            }
            
            // Mvc node
            var mvcNode = rootNode as MvcSiteMapNode;

            // Check visibility
            bool nodeVisible = true;
            if (mvcNode != null)
            {
                nodeVisible = mvcNode.VisibilityProvider.IsVisible(
                    mvcNode, HttpContext.Current, SourceMetadata);

                if (!nodeVisible)
                {
                    return "";
                }
            }
            
            // Determine extra attributes
            var extraAttributes = new StringBuilder();
            if (mvcNode != null)
            {
                if (!string.IsNullOrEmpty(mvcNode.TargetFrame))
                {
                    extraAttributes.Append(" target=\"" + mvcNode.TargetFrame + "\"");
                }
                string linkTitle = "";
                if (!string.IsNullOrEmpty(mvcNode.Title))
                {
                    linkTitle = mvcNode.Title;
                }
                if (!string.IsNullOrEmpty(mvcNode.Description))
                {
                    linkTitle = mvcNode.Description;
                }
                if (!string.IsNullOrEmpty(linkTitle))
                {
                    extraAttributes.Append(" title=\"" + linkTitle + "\"");
                }
            }

            // Render root node
            sb.Append("<li>");
            if (mvcNode != null && mvcNode.Clickable == false)
            {
                sb.Append(rootNode.Title);
            }
            else
            {
                sb.Append(string.Format("<a href=\"{0}\"{1}>{2}</a>", rootNode.Url, extraAttributes, rootNode.Title));
            }
            if (rootNode.HasChildNodes)
            {
                // Make sure all child nodes are accessible prior to rendering them...
                var shouldRender = true;
                foreach (SiteMapNode node in rootNode.ChildNodes)
                {
                    // Check visibility
                    mvcNode = node as MvcSiteMapNode;
                    if (mvcNode != null)
                    {
                        shouldRender = mvcNode.VisibilityProvider.IsVisible(
                            mvcNode, HttpContext.Current, SourceMetadata);
                    }

                    // Check ACL
                    if (!node.IsAccessibleToUser(HttpContext.Current))
                    {
                        shouldRender = false;
                        break;
                    }
                }

                // Render child nodes?
                if (shouldRender)
                {
                    sb.AppendLine();
                    foreach (SiteMapNode node in rootNode.ChildNodes)
                    {
                        if (node.IsAccessibleToUser(HttpContext.Current))
                        {
                            if (renderRootInChildLevel)
                            {
                                sb.Append(SiteMap(helper, node, "", false));
                            }
                            else
                            {
                                sb.Append(SiteMap(helper, node, "", rootNode.HasChildNodes));
                            }
                        }
                    }
                }
            }
            sb.AppendLine("</li>");

            // Close unordered list tag
            if (renderListTag)
            {
                sb.AppendLine("</ul>");
            }

            return sb.ToString();
        }
    }
}
